#pragma once

// 编写日志

#include <iostream>
#include <fstream>
#include <cstdio>
#include <string>
#include <ctime>
#include <cstdarg>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
#include "LockGuard.hpp"

bool gIsSave = false;                  // 是否保存到文件中
std::string logname = "log.txt"; // 保存日志的文件

// 日志等级
enum Level
{
    DEBUG = 0, // 调试
    INFO,      // 常规信息
    WARNING,   // 警告
    ERROR,     // 出错
    FATAL      // 致命错误
};

// 日志等级转为字符串
std::string LevelToString(int level)
{
    switch (level)
    {
    case DEBUG:
        return "Debug";
    case INFO:
        return "Info";
    case WARNING:
        return "Warning";
    case ERROR:
        return "Error";
    case FATAL:
        return "Fatal";
    default:
        return "Unknown";
    }
}

// 保存到日志文件中
void SaveFile(std::string &filename, const std::string &message)
{
    // 创建文件流，并尝试打开文件，追加模式下写入
    std::ofstream out(filename, std::ios::app);

    // 检查文件是否打开成功
    if (!out.is_open())
        return; // 打开失败

    out << message; // 写入数据
    out.close();    // 关闭文件流
}

// 获取当前时间，并转为字符串
std::string GetTimeString()
{
    // 获取当前时间
    time_t curr_time = time(nullptr);

    // 将时间转为时间结构体
    struct tm *format_time = localtime(&curr_time);

    // 转换失败，可能为无效时间
    if (format_time == nullptr)
        return "None";

    char time_buffer[1024];

    // 转为想要的字符串格式放入缓冲区中
    snprintf(time_buffer, sizeof(time_buffer), "%d-%d-%d %d-%d-%d",
             format_time->tm_year + 1900,
             format_time->tm_mon + 1,
             format_time->tm_mday,
             format_time->tm_hour,
             format_time->tm_min,
             format_time->tm_sec);

    return time_buffer;
}

pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
// 日志文件，行数，是否保存到文件中，日志等级，想要输入的字符串（可变参数）
void LogMessage(std::string filename, int line, bool issave, int level, const char *format, ...)
{
    std::string levelstr = LevelToString(level); // 等级字符串
    std::string timestr = GetTimeString();       // 时间字符串
    pid_t selfpid = getpid();

    // 处理可变参数
    char buffer[1024];
    va_list arg;           // 声明可变参数列表
    va_start(arg, format); // 初始化arg，arg从format中读可变参数

    // 按照 format 给的方式，格式控制字符串，并写入缓冲区（arg包含了format的参数列表）
    // 比如 ("helloworld %d %d",cnt,num),format 就是helloworld %d %d，参数列表就是cnt,num
    vsnprintf(buffer, sizeof(buffer), format, arg);

    // 清理arg
    va_end(arg);

    std::string message = "[" + timestr + "]" + "[" + levelstr + "]" +
                          "[" + std::to_string(selfpid) + "]" +
                          "[" + filename + "]" + "[" + std::to_string(line) + "]" + buffer;

    LockGuard lockguard(&lock); // 保护显示器资源，防止输出混乱

    if (!issave)
        std::cout << message; // 向显示器输出
    else
        SaveFile(logname, message); // 写入文件
}

// 宏定义，便于日志的使用,##__VA_ARGS__可以确保在没有提供额外参数的情况下不会留下多余的逗号
#define LOG(level, format, ...)                                             \
    do                                                                       \
    {                                                                        \
        LogMessage(__FILE__, __LINE__, gIsSave, level, format, ##__VA_ARGS__); \
    } while (0)

// 日志存入文件
#define EnableFile()    \
    do                  \
    {                   \
        gIsSave = true; \
    } while (0)

// 日志输出到屏幕
#define EnableScreen()   \
    do                   \
    {                    \
        gIsSave = false; \
    } while (0)